package com.ruoyi.business.service.impl;

import com.ruoyi.business.service.IProcessService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

@Service
public class ProcessServiceImpl implements IProcessService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;

    @Override
    public Deployment deploy(String filePath) throws IOException {

        // 对传入的文件类型进行判断
        String suffix = filePath.substring(filePath.lastIndexOf(".") + 1);
        Deployment deployment = null;
        if ("zip".equalsIgnoreCase(suffix)){
            deployment = repositoryService.createDeployment()
                    .addZipInputStream(new ZipInputStream(Files.newInputStream(Paths.get(filePath))))
                    .deploy();
        }else {
            deployment = repositoryService.createDeployment()
                    .addInputStream(filePath, Files.newInputStream(Paths.get(filePath)))
                    .deploy();
        }
        return deployment;
    }

    @Override
    public ProcessDefinition selectDefinitionProcessByDeploymentId(String deploymentId) {
        return repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();
        //return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
    }

    @Override
    public InputStream getBpmnInputStream(String deployId) {
        // 传入部署id,bpmn的文件名,可以获取到bpmn的输入流信息
        return repositoryService.getResourceAsStream(deployId,getProcessDefinitionByDeployId(deployId).getResourceName());
    }

    @Override
    public InputStream getPngInputStream(String deployId, List<String> highLightedActivities,
                                         List<String> highLightedFlows) {
        ProcessDefinition processDefinition = getProcessDefinitionByDeployId(deployId);
        BpmnModel model = repositoryService.getBpmnModel(processDefinition.getId());
        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        //generateDiagram(流程模型,需要高亮的节点,需要高亮的线条,后面三个参数都表示是字体)
        InputStream inputStream = generator.generateDiagram(model, highLightedActivities, highLightedFlows,"宋体","宋体","宋体");
        return inputStream;
    }

    public ProcessDefinition getProcessDefinitionByDeployId(String deployId) {
        return repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployId).singleResult();
    }

    @Override
    public ProcessInstance startProcessInstance(String definitionKey, String businessKey, Map<String, Object> variables) {

        return runtimeService.startProcessInstanceByKey(definitionKey,businessKey,variables);
    }

    @Override
    public Task getTaskByProcessInstanceId(String id) {

        return taskService.createTaskQuery().processInstanceId(id).singleResult();
    }

    @Override
    public void addCandidateUser(Task currentTask, Long userId) {

        taskService.addCandidateUser(currentTask.getId(),String.valueOf(userId));
    }

    @Override
    public InputStream getProcessImgByProcessInstanceId(Long instanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId.toString()).singleResult();
        String processDefinitionId = null;
        List<String> activeActivityIds = null;
        if (Objects.nonNull(processInstance)) {
            processDefinitionId = processInstance.getProcessDefinitionId();
            activeActivityIds = runtimeService.getActiveActivityIds(instanceId.toString());

        }else {
            // 查询流程已经结束的流程图
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(instanceId.toString()).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(instanceId.toString()).list();
            activeActivityIds = list.stream().map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());
        }

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        // 高亮线
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instanceId.toString())
                .orderByHistoricActivityInstanceId().asc().list();
        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historicActivityInstances);// 获取处理过的连接线
        //generateDiagram(流程模型,需要高亮的节点,需要高亮的线条,后面三个参数都表示是字体)
        InputStream inputStream = generator.generateDiagram(bpmnModel, activeActivityIds, highLightedFlows,"宋体","宋体","宋体");
        return inputStream;

    }

    @Override
    public void deleteProcessInstanceByInstanceId(String instanceId) {
        //执行此方法后未审批的任务 act_ru_task 会被删除，流程历史 act_hi_taskinst 不会被删除，并且流程历史的状态为finished完成
        runtimeService.deleteProcessInstance(instanceId,"用户撤销");
    }

    @Override
    public Task getTaskByTaskId(String taskId) {
        return taskService.createTaskQuery().taskId(taskId).singleResult();
    }

    @Override
    public void claimAndComplete(Task task, String userId, Boolean auditStatus, String comment) {

        String commentStr = auditStatus ? "[同意]":"[拒绝]";
        commentStr += comment;
        // 1.给当前任务添加批注信息
        taskService.addComment(task.getId(),task.getProcessInstanceId(),commentStr);
        // 2.认领任务
        taskService.claim(task.getId(),userId);
        // 3.执行任务,把流程变量传入
        /*Map<String,Object> variables = new HashMap<>();
        variables.put(task.getTaskDefinitionKey(),auditStatus);*/
        //taskService.complete(task.getId(),variables);
        taskService.setVariable(task.getId(),task.getTaskDefinitionKey(),auditStatus);
        taskService.complete(task.getId());
    }

    @Override
    public long selectTodoTaskCount(String userId, String definitionKey) {

        return taskService.createTaskQuery().processDefinitionKey(definitionKey)
                .taskCandidateUser(userId)
                .count();
    }

    @Override
    public List<Task> selectTodoTaskList(String definitionKey, String candidateUser, int firstResult, Integer pageSize) {

        return taskService.createTaskQuery()
                .processDefinitionKey(definitionKey)
                .taskCandidateUser(candidateUser)
                .orderByTaskCreateTime()
                .desc()
                .listPage(firstResult,pageSize);
    }

    @Override
    public ProcessInstance getProcessInstanceById(String processInstanceId) {
        return runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    }

    @Override
    public void complete(String taskId) {
        taskService.complete(taskId);
    }

    @Override
    public void claimAndComplete(Task task, Long userId, String comment) {
        // 1.给当前任务添加批注信息
        taskService.addComment(task.getId(),task.getProcessInstanceId(),comment);
        // 2.认领任务
        taskService.claim(task.getId(),userId.toString());

        taskService.complete(task.getId());
    }

    @Override
    public void setVariable(String taskId, String money, long longValue) {

        taskService.setVariable(taskId,money,longValue);
    }

    @Override
    public long getHistoricTaskCount(String definitionKey, String userId) {

        return historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(definitionKey)
                .taskAssignee(userId)
                .finished()
                .count();
    }

    @Override
    public List<HistoricTaskInstance> selectHistoricTaskList(String definitionKey, String userId, Integer starNum, Integer pageSize) {

        return historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(definitionKey)
                .taskAssignee(userId)
                .finished()
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .list();
    }

    @Override
    public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {

        return historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    @Override
    public List<HistoricActivityInstance> selectHistoryTaskList(Long instanceId) {

        return historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId.toString())
                .activityType("userTask").finished().orderByHistoricActivityInstanceStartTime()
                .desc().list();
    }

    @Override
    public String getTaskComment(String taskId) {
        List<Comment> taskComments = taskService.getTaskComments(taskId, "comment");
        if(taskComments!=null && taskComments.size()>0){
            return taskComments.get(0).getFullMessage();
        }else{
            return "";
        }
    }


    private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();

            /**
             * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转： 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(targetFlowNode.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }

                    highLightedFlowIds.add(highLightedFlowId);
                }

            }

        }
        return highLightedFlowIds;
    }

}
